using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Security.Cryptography;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.DataAccess.Common;
using T.FAS.Runtime.DataAccess.SPI;
using T.FAS.Runtime.DataAccess.SQLDom;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using static T.FAS.Runtime.Metadata.Entity.DataModel.CompositeDataObjectRefSchemaEntity;
using Meta = T.FAS.Runtime.Metadata.Entity.DataModel;

namespace T.FAS.Runtime.DataAccess.Engine
{
    /// <summary>
    /// 数据上下文构造类
    /// </summary>
    class DataContextBuilder
    {
        /// <summary>
        /// 构造数据上下文工厂方法，不支持Update方法
        /// </summary>
        /// <typeparam name="T">data数据类型，支持DataTable等</typeparam>
        /// <param name="cdo">复合数据对象</param>
        /// <param name="cno">复合数据对象节点对象</param>
        /// <param name="opType">CRUD类型，不支持Update，Update单独走逻辑</param>
        /// <param name="data">data数据</param>
        /// <returns></returns>
        public static DataContext CreateDataContext<T>(CompositeDataObject cdo, CompositeNodeObject cno, DataAccessOpType opType, T data, SqlExecuteControlContext controlContext = null)
        {
            DataContext dataContext = null;
            switch (opType)
            {
                case DataAccessOpType.I:
                    dataContext = CreateSaveContext(cdo, cno, data, controlContext);
                    break;
                case DataAccessOpType.Q:
                    dataContext = CreateQueryContext(cdo, cno, data, controlContext);
                    break;
                case DataAccessOpType.D:
                    dataContext = CreateDeleteContext(cdo, cno, data as IDictionary<string, object>, controlContext);
                    break;
                case DataAccessOpType.U:    //更新采用单独逻辑处理
                default:
                    break;
            }
            return dataContext;
        }

        /// <summary>
        /// 构造Update操作的上下文
        /// </summary>
        /// <typeparam name="T">入参数据类型</typeparam>
        /// <param name="cdo">模型</param>
        /// <param name="cno">节点</param>
        /// <param name="data">入参数据</param>
        /// <returns></returns>
        public static UpdateDataContext CreateDataContextForUpdate<T>(CompositeDataObject cdo, CompositeNodeObject cno, T data, SqlExecuteControlContext controlContext = null)
        {
            var dataContext = CreateUpdateContextColle(cdo, cno, data, controlContext);
            return dataContext;
        }

        #region Update Build Context
        /// <summary>
        /// 低代码平台改动：修改操作执行单独的DataContext Build逻辑，因为子表中会同时存在增删改三种操作
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="cdo"></param>
        /// <param name="cno"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        private static UpdateDataContext CreateUpdateContextColle<T>(CompositeDataObject cdo, CompositeNodeObject cno, T data, SqlExecuteControlContext controlContext = null)
        {
            UpdateDataContext updateDataContext = new UpdateDataContext();
            UpdateDataContextSourceData UpdateDataContextSourceData = null;
            if (data is ListTRecord listTRecord)
            {
                UpdateDataContextSourceData = new DataContextUtil().ConvertForUpdate(cdo, listTRecord, controlContext);
            }
            else if (data is TRecord record)
            {
                UpdateDataContextSourceData = new DataContextUtil().ConvertForUpdate(cdo, record, controlContext);
            }

            //构造更新上下文
            if (UpdateDataContextSourceData.UpdateData.Count > 0)
            {
                updateDataContext.UpdateContext = CreateSaveUpdateContext(cdo, cno, UpdateDataContextSourceData.UpdateData, DataAccessOpType.U, controlContext);
            }
            //构造插入上下文
            if (UpdateDataContextSourceData.InsertData.Count > 0)
            {
                updateDataContext.InsertContext = CreateSaveUpdateContext(cdo, cno, UpdateDataContextSourceData.InsertData, DataAccessOpType.I, controlContext);
            }
            #region 构造子节点集合的删除上下文
            if (UpdateDataContextSourceData.DeleteData.Keys.Count > 0)  //指定节点的直接子级节点集合
            {
                var allChildNodes = cno.GetAllChildNodes(cdo);
                if (allChildNodes != null && allChildNodes.Count > 0)
                {
                    foreach (var childCNO in allChildNodes)
                    {
                        if (!UpdateDataContextSourceData.DeleteData.Keys.Contains(childCNO.ID))
                            continue;
                        var pkEle = childCNO.GetPKElement();
                        foreach (var DelKey in UpdateDataContextSourceData.DeleteData[childCNO.ID])
                        {
                            var delContext = CreateDeleteContext(cdo, childCNO, new Dictionary<string, object> { { pkEle.ID, DelKey } }, controlContext);
                            delContext.EntryCompositeNodeObject = childCNO;
                            updateDataContext.DeleteContext.Add(delContext);
                        }
                    }
                }
            }
            #endregion
            return updateDataContext;
        }

        /// <summary>
        /// 使用List_DataTable 构造上下文
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="cno"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        private static DataContext CreateSaveUpdateContext(CompositeDataObject cdo, CompositeNodeObject cno, object data, DataAccessOpType type, SqlExecuteControlContext controlContext = null)
        {
            var context = new DataContext()
            {
                MainCompositeNodeObject = cno,
            };
            if (type == DataAccessOpType.I)
                ProcessSaveContext(context.Data, cno, data as List<DataTable>, type, true, controlContext);
            else if (type == DataAccessOpType.U)
                ProcessUpdateContext(context.Data, cno, data as Dictionary<string, List<UpdateChangeSet>>, type, true, controlContext);
            return context;
        }

        /// <summary>
        /// 递归所有节点生成DataContext.Data内容
        /// </summary>
        /// <param name="dict"></param>
        /// <param name="cno"></param>
        /// <param name="data"></param>
        /// <param name="isRecursive"></param>
        private static void ProcessSaveContext(Dictionary<string, List<DataContextItem>> dict, CompositeNodeObject cno, List<DataTable> data, DataAccessOpType type, bool isRecursive = true, SqlExecuteControlContext controlContext = null)
        {
            DataTable dt = data.Find(t => string.Equals(t.TableName, cno.ID));
            if (dt == null)
                goto JumpNoDataNode;    //低代码改动:Tag:Update,父节点如果未找到DT数据源，依然遍历子节点进行处理
            var items = new List<DataContextItem>(dt.Rows.Count);
            foreach (DataRow row in dt.Rows)
            {
                items.Add(GetModelObjectPropValueNew(row, cno, type, type != DataAccessOpType.I, controlContext));
            }
            dict.Add(cno.ID, items);
        JumpNoDataNode:
            if (cno.ChildCompositeNodeObjects == null || cno.ChildCompositeNodeObjects.Count == 0)
                return;
            foreach (var childCompositeNodeObject in cno.ChildCompositeNodeObjects)
            {
                ProcessSaveContext(dict, childCompositeNodeObject, data, type, isRecursive, controlContext);
            }
        }

        /// <summary>
        /// 递归所有节点生成DataContext.Data内容
        /// </summary>
        /// <param name="dict"></param>
        /// <param name="cno"></param>
        /// <param name="data"></param>
        /// <param name="isRecursive"></param>
        private static void ProcessUpdateContext(Dictionary<string, List<DataContextItem>> dict, CompositeNodeObject cno, Dictionary<string, List<UpdateChangeSet>> data, DataAccessOpType type, bool isRecursive = true, SqlExecuteControlContext controlContext = null)
        {
            var key = data.Keys.FirstOrDefault(t => string.Equals(t, cno.ID));
            if (key != null)
            {
                var changeSets = data[key];
                if (changeSets != null)
                {
                    var items = new List<DataContextItem>(changeSets.Count);
                    foreach (UpdateChangeSet changeSet in changeSets)
                    {
                        items.Add(GetModelObjectPropValueNew(changeSet, cno, type, type != DataAccessOpType.I, controlContext));
                    }
                    dict.Add(cno.ID, items);
                }
            }
            if (cno.ChildCompositeNodeObjects == null || cno.ChildCompositeNodeObjects.Count == 0)
                return;
            foreach (var childCompositeNodeObject in cno.ChildCompositeNodeObjects)
            {
                ProcessUpdateContext(dict, childCompositeNodeObject, data, type, isRecursive, controlContext);
            }
        }
        #endregion

        #region Create Build Context
        /// <summary>
        /// 构造Create上下文
        /// </summary>
        /// <typeparam name="T">data数据类型，支持DataTable等</typeparam>
        /// <param name="cdo">复合数据对象</param>
        /// <param name="cno">复合数据对象节点对象</param>
        /// <param name="opType">CRUD类型</param>
        /// <param name="data">data数据</param>
        /// <returns></returns>
        private static DataContext CreateSaveContext<T>(CompositeDataObject cdo, CompositeNodeObject cno, T data, SqlExecuteControlContext controlContext = null)
        {
            if (data == null)
                return default;
            var opType = DataAccessOpType.I;
            //当T为ListTRecord时
            if (data is ListTRecord listTRecord)
            {
                var dt = new DataContextUtil().ConvertToDataTableForInsert(cdo, listTRecord, controlContext);
                return CreateSaveUpdateContext(cdo, cno, dt, opType, controlContext);
            }
            //当T为record时
            else if (data is TRecord record)
            {
                var dt = new DataContextUtil().ConvertToDataTableForInsert(cdo, record, controlContext);
                return CreateSaveUpdateContext(cdo, cno, dt, opType, controlContext);
            }
            else return default;
        }
        #endregion

        #region Query Build Context
        /// <summary>
        /// 构造查询上下文
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <param name="CompositeNodeObject"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        protected static DataContext CreateQueryContext<T>(CompositeDataObject model, CompositeNodeObject CompositeNodeObject, T data, SqlExecuteControlContext controlContext = null)
        {
            var context = new DataContext() { MainCompositeNodeObject = CompositeNodeObject };
            var dataContextItem = new DataContextItem()
            {
                OpType = DataAccessOpType.Q
            };

            if (data is IDictionary<string, object>)
                dataContextItem.PrimaryKeyData = data as IDictionary<string, object>;
            else if (data is QueryFilter)
                context.QueryFilter = data as QueryFilter;

            context.Add(CompositeNodeObject.ID, new List<DataContextItem>() { dataContextItem });

            return context;
        }
        #endregion

        #region Delete Build Context
        protected static DataContext CreateDeleteContext(CompositeDataObject model, CompositeNodeObject CompositeNodeObject, IDictionary<string, object> pkData, SqlExecuteControlContext controlContext = null)
        {
            var context = new DataContext() { MainCompositeNodeObject = CompositeNodeObject };
            var dataContextItem = new DataContextItem()
            {
                OpType = DataAccessOpType.D
            };

            dataContextItem.PrimaryKeyData = pkData;
            context.Add(CompositeNodeObject.ID, new List<DataContextItem>() { dataContextItem });

            return context;
        }
        #endregion

        /// <summary>
        /// 将data生成DataContextItem中的数据
        /// </summary>
        /// <param name="recordValueData">一行记录的Data</param>
        /// <param name="cno"></param>
        /// <param name="opType"></param>
        /// <param name="isUseDbNull"></param>
        /// <returns></returns>
        private static DataContextItem GetModelObjectPropValueNew(object recordValueData, CompositeNodeObject cno, DataAccessOpType opType, bool isUseDbNull, SqlExecuteControlContext controlContext = null)
        {
            var dataContextItem = new DataContextItem()
            {
                OpType = opType
            };
            //多语言映射
            Dictionary<string, string> mapping = InternationalTools.GetLanguageMapping(controlContext.InternationalMode, controlContext.SpecifiedLanguages);
            foreach (var element in cno.Elements)
            {
                if (element.ElementType == ElementType.Reference) continue;
                Meta.DataColumn dataColumn = element.DataColumn ?? cno.DataObject.Columns.First(col => col.ID == element.DataColumnID);
                bool isPkElement = dataColumn.IsPkColumn;

                #region 额外控制逻辑，预校验
                //update类型只应该生成data中有的列
                if (opType == DataAccessOpType.U)
                {
                    if (recordValueData is UpdateChangeSet changeSet)
                    {
                        //国际化字段与掩码字段额外处理
                        if (!dataColumn.IsInternational && !dataColumn.MaskingRule.IsEnable)
                        {
                            //update时，只处理ChangeSet.ChangeSetItems中存在的列，不存在的跳过
                            TChangeSetItem item = changeSet.ChangeSetItems.FirstOrDefault(t => t.ColumnCode == dataColumn.ColumnName);
                            if (item == null)
                                continue;
                        }
                    }
                    //不允许修改系统默认参数，LastModifyTime放开由后端代码赋值
                    if (element.DataColumn.IsSystemField() && !element.DataColumn.IsTimestampField())
                        continue;

                    //跳过处理只读的字段，不会修改其值
                    if (controlContext?.ReadonlyFields != null && controlContext.ReadonlyFields.Count > 0)
                    {
                        if (controlContext.ReadonlyFields.Contains(dataColumn.ColumnName)
                            && !dataColumn.IsPkColumn
                            && !element.DataColumn.IsTimestampField())
                            continue;
                    }
                }
                if (opType == DataAccessOpType.I)
                {
                    //不允许修改系统默认参数，LastModifyTime放开由后端代码赋值
                    if (element.DataColumn.IsSystemField())
                        continue;
                }
                #endregion

                //是否启用国际化
                if (dataColumn.IsInternational)
                {
                    //额外校验，国际化列只能作用于普通的string列
                    if (element.DataType == DataType.String && !dataColumn.IsPkColumn)
                    {
                        if (opType == DataAccessOpType.I)
                        {
                            FillDataContextItem(dataContextItem, cno.DataObject, element, dataColumn, recordValueData);

                            if (mapping.Count > 0)
                            {
                                foreach (string langKey in mapping.Keys)
                                {
                                    //update类型处理待定
                                    //string langElementID = InternationalTools.GetRealFieldName(cno.DataObject, element.ID, langKey);
                                    string langElementID = InternationalTools.GetDataItemElementIDKey(cno.DataObject, element, langKey);
                                    object i18NFieldValue = ObjectPropertyValueUtils.GetPropValue(cno.DataObject, element, dataColumn, recordValueData, langKey);
                                    if (i18NFieldValue != null && i18NFieldValue != DBNull.Value)
                                    {
                                        dataContextItem.Data.Add(langElementID, i18NFieldValue);
                                    }
                                    else
                                    {
                                        //辅助语言列如果未赋值，不作处理直接跳过
                                        continue;
                                    }
                                }
                            }
                        }
                        else if (opType == DataAccessOpType.U)
                        {
                            if (recordValueData is UpdateChangeSet changeSet)
                            {
                                //处理主语言列
                                TChangeSetItem item = changeSet.ChangeSetItems.FirstOrDefault(t => t.ColumnCode == dataColumn.ColumnName);
                                if (item != null)
                                {
                                    FillDataContextItem(dataContextItem, cno.DataObject, element, dataColumn, recordValueData);
                                }
                                if (mapping.Count > 0)
                                {
                                    foreach (string langKey in mapping.Keys)
                                    {
                                        string logicFieldName = InternationalTools.GetLogicFieldName(dataColumn.ColumnName, mapping[langKey]);
                                        //处理主语言列
                                        TChangeSetItem i18NItem = changeSet.ChangeSetItems.FirstOrDefault(t => t.ColumnCode == logicFieldName);
                                        if (i18NItem == null) continue;

                                        //update类型处理待定
                                        //string langElementID = InternationalTools.GetRealFieldName(cno.DataObject, element.ID, langKey);
                                        string langElementID = InternationalTools.GetDataItemElementIDKey(cno.DataObject, element, langKey);
                                        object i18NFieldValue = ObjectPropertyValueUtils.GetPropValue(cno.DataObject, element, dataColumn, recordValueData, i18NCode: mapping[langKey]);
                                        if (i18NFieldValue != null && i18NFieldValue != DBNull.Value)
                                        {
                                            dataContextItem.Data.Add(langElementID, i18NFieldValue);
                                        }
                                        else
                                        {
                                            //辅助语言列如果未赋值，不作处理直接跳过
                                            continue;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                //统一数据访问服务掩码改造
                if (dataColumn.MaskingRule.IsEnable)
                {
                    if (opType == DataAccessOpType.I)
                    {
                        //构造主字段数据
                        FillDataContextItem(dataContextItem, cno.DataObject, element, dataColumn, recordValueData);

                        //构造掩码字段数据
                        if (MaskTool.EnableFASMaskStorage(dataColumn.MaskingRule))
                        {
                            string maskElementID = MaskTool.GetMaskDataItemElementIDKey(cno.DataObject, element);
                            object maskFieldValue = ObjectPropertyValueUtils.GetPropValue(cno.DataObject, element, dataColumn, recordValueData, isMask: true);
                            if (maskFieldValue != null && maskFieldValue != DBNull.Value)
                            {
                                dataContextItem.Data.Add(maskElementID, maskFieldValue);
                            }
                        }
                    }
                    else if (opType == DataAccessOpType.U)
                    {
                        if (recordValueData is UpdateChangeSet changeSet)
                        {
                            //处理主语言列
                            TChangeSetItem item = changeSet.ChangeSetItems.FirstOrDefault(t => t.ColumnCode == dataColumn.ColumnName);
                            if (item != null)
                            {
                                FillDataContextItem(dataContextItem, cno.DataObject, element, dataColumn, recordValueData);
                            }

                            if (MaskTool.EnableFASMaskStorage(dataColumn.MaskingRule))
                            {
                                //处理掩码列
                                var logicFieldName = Meta.DataColumn.GetMaskColumnName(cno.DataObject, element.DataColumn.ColumnName);
                                TChangeSetItem maskItem = changeSet.ChangeSetItems.FirstOrDefault(t => t.ColumnCode == logicFieldName);
                                if (maskItem != null)
                                {
                                    //update类型处理待定
                                    string maskElementID = MaskTool.GetMaskDataItemElementIDKey(cno.DataObject, element);
                                    object maskFieldValue = ObjectPropertyValueUtils.GetPropValue(cno.DataObject, element, dataColumn, recordValueData, isMask: true);
                                    if (maskFieldValue != null && maskFieldValue != DBNull.Value)
                                    {
                                        dataContextItem.Data.Add(maskElementID, maskFieldValue);
                                    }
                                }
                            }
                        }
                    }
                }
                if (!dataColumn.IsInternational && !dataColumn.MaskingRule.IsEnable)
                {
                    //Insert类型字段值为null时忽略，使其使用DB默认值
                    object propValue = ObjectPropertyValueUtils.GetPropValue(cno.DataObject, element, dataColumn, recordValueData);
                    if (propValue != null && propValue != DBNull.Value)
                    {
                        if (element.DataType == DataType.Int)
                        {
                            if (int.TryParse(propValue?.ToString(), out int realValue))
                            {
                                propValue = realValue;
                            }
                        }
                        dataContextItem.Data.Add(element.ID, propValue);
                        if (isPkElement)
                        {
                            dataContextItem.PrimaryKeyData.Add(element.ID, propValue);
                        }
                    }
                    else
                    {
                        if (isPkElement)
                        {
                            var opTypeStr = opType == DataAccessOpType.U ? "修改" : "新增";
                            throw new FasException(DataAccessErrorCode.CompositeDataObject.Error, string.Format(CommonStringClassEngine.DataContextBuilder_GetModelObjectPropValueNew,cno.CompositeDataObject?.Code,cno.DataObjectTableAlias,opTypeStr));
                        }
                        if (opType == DataAccessOpType.I)
                        {
                            var isHaveDefaultValue = !string.IsNullOrEmpty(dataColumn.DefaultValue);
                            //跳过存在DB默认值且入参值为空的字段，让其使用数据库默认值
                            if (isHaveDefaultValue)
                            {
                                continue;
                            }
                            LocalLogService.Write($"DataContextBuilder构造DataContextItem，DMO:{cno.ID}, ELE:{element.ID}, ColumnName:{dataColumn.ColumnName} 未获取到数据 ");
                            //不校验是否可空的参数，如果数据表中该列没有默认值，也会抛出异常
                            //如果该元素为非可空类型，且在Data中找不到该元素，则抛出异常
                        }
                        dataContextItem.Data.Add(element.ID, DBNull.Value);
                    }
                }
            }
            return dataContextItem;
        }

        /// <summary>
        /// 填充普通列的DataContextItem，对于国际化列、掩码列需要额外处理
        /// </summary>
        /// <param name="dataContextItem"></param>
        /// <param name="dataObject"></param>
        /// <param name="element"></param>
        /// <param name="dataColumn"></param>
        /// <param name="recordValueData"></param>
        public static void FillDataContextItem(DataContextItem dataContextItem, DataObject dataObject, CompositeNodeObjectElement element, Meta.DataColumn dataColumn, object recordValueData)
        {
            object mainFieldValue = ObjectPropertyValueUtils.GetPropValue(dataObject, element, dataColumn, recordValueData);
            if (mainFieldValue != null && mainFieldValue != DBNull.Value)
            {
                //防止同时启用国际化&掩码列时，重复处理主列报错
                dataContextItem.Data[element.ID] = mainFieldValue;
            }
            else
            {
                //国际化字段不启用默认值
                dataContextItem.Data[element.ID] = DBNull.Value;
            }
        }
    }

    #region class

    /// <summary>
    /// DataTable模板
    /// </summary>
    class TableDataTemplete
    {
        public string CompositeNodeObject { get; set; }
        public DataTable Data { get; set; }
        public static List<DataTable> GetDataTables(List<TableDataTemplete> list)
        {
            List<DataTable> dataTables = new List<DataTable>();
            foreach (var t in list)
            {
                t.Data.TableName = t.CompositeNodeObject;
                dataTables.Add(t.Data);
            }
            return dataTables;
        }
    }

    /// <summary>
    /// 修改类型操作的上下文结构
    /// </summary>
    public class UpdateDataContext
    {
        public List<DataContext> DeleteContext { get; set; } = new List<DataContext>();
        public DataContext UpdateContext { get; set; } = new DataContext();
        public DataContext InsertContext { get; set; } = new DataContext();
    }

    /// <summary>
    /// 
    /// </summary>
    public class UpdateChangeSet
    {
        public DateTime LastModifyTime { get; set; }
        public List<TChangeSetItem> ChangeSetItems { get; set; }
    }
    #endregion
}
